package org.one.stone.soup.grfxML;

import java.util.*;
import java.io.*;

import org.one.stone.soup.browser.*;
import org.one.stone.soup.stringhelper.StringInputStream;
import org.one.stone.soup.xml.*;

/*
 * Copyright 2000-2001 Wet-Wired.com Ltd.,
 * Portsmouth England
 * All rights reserved.
 */

/**
	* The APIInterface class allows access to grfxML browser functions
	* not available through accessing <code>Data</code> classes.
	* All available methods are static and can be accessed from any
	* Java class.
	* @author Nik Cross
*/

import java.net.*;
import java.awt.*;

import org.one.stone.soup.mjdb.data.field.*;

public final class APInterface{

	private static Hashtable engines = new Hashtable();
	private GrfxMLEngine engine = null;

	private boolean autoCommit = true;
	/**
	*	This method will add any grfxML element to an
	*	array container. If autoCommit is set true
	*	(default = true) then the grfxML model will
	*	be rebuilt to include the new data.
	*
	* @param newTag		The grfxML element to be added
	* @param target		The grfxML container for the element to be
	*					added to
	*/
	public APInterface(GrfxMLEngine engine,String alias)
	{
		this.engine = engine;
		engines.put(alias,this);
	}

	public static APInterface getApi(String engineAlias)
	{
		return (APInterface)engines.get(engineAlias);
	}

public  void add(Field newTag , ArrayField target)
{
	try{
		target._add(newTag,newTag);
		target.register(engine.BASE._DATA);
		newTag.register(engine.BASE._DATA);

		if(autoCommit)
			commit();
	}catch(LockException le){}//dummy catch
}
	/**
	 * This method is called to move the grfxML browser back one page.
	 * It is has the same effect as the <code>anchorBack</code> tag.
	 */

public  void back()
{
	engine.errors=false;
	engine.pause=false;
	engine.unloadPage();
}
	/**
	*	This method will return an instance of the prototype named
	*	'prototypeName' with an id 'id'.
	*
	* @param prototypeName	the name of the Prototype to build
	* @param id				the name for the new instance of the prototype
	* @return DataNode		the new instance
	*/
public  DataNode buildPrototype(String prototypeName, String id,DataNode parent)
{
	try{
		DataNode newNode = new DataNode(engine,null);
		newNode.setId( id,newNode );
		GrfxMLPrototypeBuilder.buildPrototype(engine,newNode,prototypeName,parent);
		newNode.register(engine.BASE._DATA);

		return newNode;
	}catch(LockException le){}//dummy catch

	return null;
}
	/**
	*	This method will build an instance of the prototype named
	*	'prototypeName' with an id 'id' and add it to the DataChildren
	*	element.
	*
	* @param prototypeName	the name of the Prototype to build
	* @param id				the name for the new instance of the prototype
	* @param target			the children tag to add the new instance to
	* @return DataNode		the new instance
	*/

public  DataNode buildPrototype(String prototypeName, String id , DataChildren target)
{
	try{
		DataNode newNode = new DataNode(engine,null);
		newNode.setId( id,newNode );
		GrfxMLPrototypeBuilder.buildPrototype(engine,newNode,prototypeName,target.parent);

		target.add( newNode,newNode );
		target.register(engine.BASE._DATA);
		newNode.register(engine.BASE._DATA);

		if(autoCommit)
			commit();

		return newNode;
	}catch(LockException le){}//dummy catch

	return null;
}

public  DataNode buildPrototype(String prototypeData, String id, int x, int y, DataChildren target,boolean markAsPrototype)
{
	try{
		XmlElement xPrototype = null;
		try{
			xPrototype = getXMLParser().parseElement( new StringInputStream(prototypeData) );
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return null;
		}

		String nodeId = xPrototype.getAttributeValueByName("id");
		if(nodeId!=null)
		{
			id = nodeId+id;
		}
		else
		{
			id = "node"+id;
		}

		DataNode prototype = new DataNode(engine,null);
		prototype.buildFromgrfxML(xPrototype);

		DataNode newNode = new DataNode(engine,null);
		newNode.setId( id,newNode );
		GrfxMLPrototypeBuilder.buildPrototype(engine,newNode,prototype,target.parent,markAsPrototype);
		newNode.parent = target.parent;

		target.add( newNode,newNode );
		target.register(engine.BASE._DATA);
		newNode.register(engine.BASE._DATA);

		DataPoint position = (DataPoint)getField( id+".position" );
		if(position!=null)
		{
			position.setValueX(x,newNode);
			position.setValueY(y,newNode);
		}

		if(autoCommit)
			commit();

		return newNode;
	}catch(LockException le){}//dummy catch

	return null;
}

	/**
	*	This method is called to force a rebuild of the
	*	grfxML model after elements have been added. It
	*	only needs to be called explicitly if autoCommit has
	*	been set to false.
	*/
public  void commit()
{
	engine.BASE._DATA = GrfxMLStoreProcessor.initialize(engine.BASE._DATA);
/*	if(engine.DEBUG) System.out.println("commit.init store done");
	engine.doInitialTranslation();*/
	if(GrfxMLEngine.DEBUG) System.out.println("commit.register done");
	engine.register();
	if(GrfxMLEngine.DEBUG) System.out.println("commit.translate done");
	engine.buildPipe();
	if(GrfxMLEngine.DEBUG) System.out.println("commit.build pipe done");
	engine.initPlugins();
	if(GrfxMLEngine.DEBUG) System.out.println("commit.init plugins done");

	engine.resetModelForNextFrame();
}
	/**
	*	Get the status of autoCommit (true = on)
	*	@return boolean		The state of auto commit
	*/
public  boolean getAutoCommit() {
	return autoCommit;
}
	/**
	* 	Return
	*/
public  DataColor getBackgroundColor() {

	return engine.BASE.SETTINGS.backgroundColor;
}
	/**
	* 	Return the key grfxML element for access to key presses.
	*	@return DataKey the grfxML base KEY element
	*/
public  DataBase getBase() {
	return engine.BASE;
}

protected  GrfxMLEngine getEngine()
{
	return engine;
}
public  GrfxMLEngine getEngine(String key)
{
	//if( Browser.isKey(key) )
	//{
		return engine;
	/*}
	else
	{
		return null;
	}*/
}
public String getCurrentFileName()
{
	return engine.currentPage;
}

	/**
	* 	Return the grfxML element with the id requested. Returns null
	*   if a field with the requested id is not found.
	 * @return Field	the grfxML element with the id requested
	*/
public  Field getField(String id)
{
	return engine.BASE._DATA.findId(id);
}
	/**
	* 	Return the grfxML element with the id requested. Returns null
	*   if a field with the requested id is not found.
	 * @return Field	the grfxML element with the id requested
	*/
public  Field getSystemField(String id)
{
	return engine.SYSTEM.data.findId(id);
}
	/**
	* 	Return
	*/
public  Image getImage() {

	return engine.getView().getPreImage();
}
	/**
	* 	Return the key grfxML element for access to key presses.
	*	@return DataKey the grfxML base KEY element
	*/
public  DataKey getKey() {
	return engine.BASE.KEY;
}
	/**
	* 	Return the mouse grfxML element for access to mouse information.
	* @return DataMouse	the grfxML base MOUSE element
	*/
public  DataMouse getMouse() {
	return engine.BASE.MOUSE;
}
	/**
	* 	Return
	*/
public  Vector[] getPipes() {

	Vector[] pipes = new Vector[3];

	pipes[0] = engine.backgroundPipe;
	pipes[1] = engine.rootPipe;
	pipes[2] = engine.guiPipe;

	return pipes;
}
	/**
	* 	Return the grfxML engine profiler.
	*	This gives access to system performance information.
	* @return grfxMLProfile	the result of the users choice
	*/
public  GrfxMLProfile getProfile() {
	return engine.profile;
}
	/**
	* 	Return the store containing all named grfxML data elements.
	* @return DataStore	the grfxML data store
	*/
public  DataStore getStore()
{
	return engine.BASE._DATA;
}
	/**
	* 	Return the java.awt toolkit
	* @return Toolkit	the java.awt Toolkit
	*/
public  Toolkit getToolkit() {
	return engine.getView().getToolkit();
}
	/**
	* 	Return the grfxML XMLParser.
	* @return XmlParser	the grfxML XMLParser
	*/
public  XmlParser getXMLParser()
{
	return engine.getParser();
}
	/**
	* 	Return an XmlElement for persistent storage of information for
	*   the current page.
	* @return XmlElement	the XmlElement for this page
	*/

/*public  XmlElement getXMLSettings()
{
	XmlDocument settings = GrfxMLEngine.getSettings();
	String key = org.one.stone.soup.stringhelper.StringGenerator.convertToHtmlString( Browser.getOwner() );

	XmlElement pages = settings.getElementByName("pages");
	if(pages==null)
	{
		pages = settings.addChild("pages");
	}

	XmlElement entry = pages.getElementByName( key );
	if(entry==null)
	{
		entry = pages.addChild(key);
	}

	return entry;
}*/
//TODO

	/**
	* 	Returns a boolean value. True means the grfxML browser has
	*	focus.
	* @return boolean	a boolean value stating whether the grfxML browser has focus
	*/

public  boolean inFocus() {
	return engine.inFocus();
}
public  boolean debugOn()
{
	return GrfxMLEngine.DEBUG;
}

	/**
	*	This method is called to force all grfxML plugins
	*	that have not been initialized to run their initialize
	*	method.
	*/
public  void initPlugins()
{
	engine.initPlugins();
}
	/**
	 * This method is called to load a new page into the grfxML browser.
	 * It is has the same effect as the <code>anchorToFile</code> tag.
	 * @param      url   the URL of the page to be loaded.
	 */

public  void load(String url)
{
	try{
		if( new File(url).exists() )
		{
			try{
				String newUrl = new File(url).toURL().toString();
				url = newUrl;
			}
			catch(Exception e){}
		}
	}
	catch(Exception e){}

	GrfxMLLoader.load(engine,url);
	//engine.load(url);
}
	/**
	 * This method is called to load a new page into the grfxML browser.
	 * It is has the same effect as the <code>anchorToFile</code> tag.
	 * @param      url   the URL of the page to be loaded.
	 */
public  void load(URL url)
{
	GrfxMLLoader.load(engine,url.toExternalForm());
	//engine.load(url);
}
	/**
	 * This method is called to load a new page into the grfxML browser.
	 * It is has the same effect as the <code>anchorToFile</code> tag.
	 * @param      file   the file of the page to be loaded.
	 * @see        org.one.stone.soup.grfxML.DataFile
	 */
public  void load(DataString file)
{
	GrfxMLLoader.load(engine,file.getValue());
	//engine.load(file);
}
	/**
	 * This method is called to load some grfxML data.
	 * The data is then compiled and added to the array element
	 * in the model.
	 * @param      iStream   the InputStream from which the grfxML is to be loaded.
	 * @param      tag   the tag to be populated from the grfxML.
	 * @param      array   the array to add the tag to.
	 */

public  void loadTo(InputStream iStream,ITag tag,ArrayField array)
{
	XmlElement element;

	try{
		element = getXMLParser().parseElement( iStream );
		iStream.close();
	}catch(Exception e)
	{
		e.printStackTrace();
		return;
	}
	tag.buildFromgrfxML(element);

	try{
		array._add((Field)tag,tag);
	}catch(LockException le){}
}
	/**
	 * This method is called to load some grfxML data.
	 * The data is then compiled and added to the array element
	 * in the model.
	 * @param      iStream   the InputStream from which the grfxML is to be loaded.
	 * @param      tag   the tag to be populated from the grfxML.
	 * @param      array   the array to add the tag to.
	 */

public  void loadTo(String data,String arrayName)
{
 try{

	Field field = getField( arrayName );

	XmlElement element = getXMLParser().parseElement(data);

	if(GrfxMLEngine.DEBUG)
		System.out.println("BUILDING:"+element.toXmlBeautify());

	if(field instanceof ArrayField)
	{
		setAutoCommit( false );

		((ITag)field).buildFromgrfxML(element);

		commit();

		setAutoCommit( true );

		if(GrfxMLEngine.DEBUG)
			System.out.println("BUILT:"+((ITag)field).togrfxML(arrayName));
	}
	else
	{
		System.out.println("Field:"+arrayName+" is not an Array field.");
	}
 }catch(Exception e)
 {
	e.printStackTrace();
 }
}
	/**
	 * This method is called to load some grfxML data.
	 * The data is then compiled and added to the array element
	 * in the model.
	 * @param      url   the URL of the grfxML to be loaded.
	 * @param      tag   the tag to be populated from the grfxML.
	 * @param      array   the array to add the tag to.
	 */

public  void loadTo(String url,ITag tag,ArrayField array)
{
	XmlElement element;

	try{
		InputStream iStream = Browser.getInputStream(url);
		if(iStream==null)
		{
		    return;
		}
		element = getXMLParser().parseElement( iStream );
		iStream.close();
	}catch(Exception e)
	{
		//e.printStackTrace();
		return;
	}
	tag.buildFromgrfxML(element);

	try{
		array._add((Field)tag,tag);
	}catch(LockException le){}
}
	/**
	 * This method is called to load some grfxML data.
	 * The data is then compiled and added to the array element
	 * in the model.
	 * @param      file   the DataFile containing the URL of the page to be loaded.
	 * @param      tag   the tag to be populated from the grfxML.
	 * @param      array   array   the array to add the tag to.
	 */

public  void loadTo(DataString file,ITag tag,ArrayField array)
{
	loadTo(file.getValue(),tag,array);
}
	/**
	 * This method is called to load some grfxML data.
	 * The data is then compiled and added to the array element
	 * in the model.
	 * @param      iStream   the InputStream from which the grfxML is to be loaded.
	 * @param      tag   the tag to be populated from the grfxML.
	 * @param      array   the array to add the tag to.
	 */

public  void loadTo(XmlElement element,String arrayName)
{
 try{

	Field field = getField( arrayName );

	if(GrfxMLEngine.DEBUG)
		System.out.println("BUILDING:"+element.toXmlBeautify());

	if(field instanceof ArrayField)
	{
		setAutoCommit( false );

		((ITag)field).buildFromgrfxML(element);

		commit();

		setAutoCommit( true );

		if(GrfxMLEngine.DEBUG)
			System.out.println("BUILT:"+((ITag)field).togrfxML(arrayName));
	}
	else
	{
		System.out.println("Field:"+arrayName+" is not an Array field.");
	}
 }catch(Exception e)
 {
	e.printStackTrace();
 }
}
	/**
	 * This method is called to load some grfxML data.
	 * The data is then compiled and added to the array element
	 * in the model.
	 * @param      url   the URL of the grfxML to be loaded.
	 * @param      tag   the tag to be populated from the grfxML.
	 * @param      array   the array to add the tag to.
	 */

public  void loadTo(XmlElement element,ITag tag,ArrayField array)
{
	tag.buildFromgrfxML(element);

	try{
		array._add((Field)tag,tag);
	}catch(LockException le){}
}
	/**
	* 	This method forces the BACKGROUND to be re-rendered.
	*/
public  void refreshBackground()
{
	engine.getView().setOffscreenReady(false);
}
/**
	Register a field with the data store
	* @param	field the field to add to the data store
*/
public  void register(Field field)
{
	field.register(engine.BASE._DATA);
}
	/**
	 * This method is called to reload the current page into the grfxML browser.
	 */

public  void reload()
{
	engine.reloadPage();
}
	/**
	*	This method will remove any grfxML element from an
	*	array container. If autoCommit is set true
	*	(default = true) then the grfxML model will
	*	be rebuilt to include the new data.
	* @param      oldTag   the tag to remove.
	* @param      target   the target to remove the tag from.
	*/
public  void remove(Field oldTag , ArrayField target)
{
	try{
		try{
			target._remove(oldTag,oldTag);
		}
		catch(Exception e)
		{
			for(int loop=0;loop<target.size();loop++)
			{
				Field nextTargetOwner = target._get(loop);

				if(nextTargetOwner instanceof DataNode)
				{
					DataChildren nextTarget = ((DataNode)nextTargetOwner).getChildren();
					remove( oldTag,nextTarget );
				}
			}

			//System.out.println("Requested object not in target");
			return;
		}

		DataStore tempStore = new DataStore(engine);
		oldTag.register( tempStore );
		GrfxMLStoreProcessor.initialize(tempStore);
		tempStore.add(oldTag,oldTag);

		for(int loop=0;loop<tempStore.size();loop++)
		{
			try{
				engine.BASE._DATA._remove(tempStore.get(loop),oldTag);
			}
			catch(Exception e){}
		}
		if(autoCommit)
			commit();
	}catch(LockException le){}//dummy catch
}
	/**
	*	Sends a command as a string to the grfxML engine.
	*	 The command is engine dependant.
	*	@param  command  the command to send.
	*/

public  void sendCommand(String command)
{
	engine.getController().sendCommand(command);
}
	/**
	*	Set the status of autoCommit (true = on)
	 * @param newAutoCommit the state of autoCommit
	*/
public  void setAutoCommit(boolean newAutoCommit) {
	autoCommit = newAutoCommit;
}
protected  void setEngine(GrfxMLEngine cntrl)
{
	engine=cntrl;
}
public  void setDebug(boolean state)
{
	GrfxMLEngine.DEBUG=state;
}
/**
 * This method requests that the debug console is shown.
 */
public  void showConsole() {
	engine.getController().showConsole(true);
}
	/**
	 * This method is called to request an HTML web page is opened by
	 * the grfxML browser.
	 * It is has the same effect as the <code>anchorToWebPage</code> tag.
	 * @param      url   the URL of the web page to be opened.
	 */

public  void showWebPage(String url)
{
	engine.getController().showWebPage(url);
}
/**
 * This method requests that the user is prompted to make a choice.
 * The decision is returned as a boolean value.
 * @param text   an array of lines of text to show.
 * @return boolean	the result of the users choice
 */
public  boolean userChoose(String[] text) {
	return engine.getController().choose(text);
}
/**
 * This method requests that the user is prompted to select a file name.
 * The chosen file name is returned as a string.
 * @param label   the label to display on the dialog.
 * @param mask   the selectable files mask.
 * @return String	the chosen file
 */
public  String userGetFile(String label,String[] masks) {
	return engine.getController().userGetFile(label,masks);
}

	/**
	 * This method is called to load a new page into the grfxML browser.
	 * It is has the same effect as the <code>anchorToFile</code> tag.
	 * @param      url   the URL of the page to be loaded.
	 */
public  void load(InputStream iStream,String resourceName)
{
	engine.load(iStream,resourceName);
}
}
